/***************************************************************************\
 * Copyright (C) by Keio University
 * Camera.hpp created in 09 2011.
 * Mail : fdesorbi@hvrl.ics.keio.ac.jp
 *
 * Camera.hpp is part of the HVRL Engine Library.
 *
 * The HVRL Engine Library is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * The HVRL Engine Library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
\***************************************************************************/

#ifndef __OPENGL__CAMERA_HPP__
#define __OPENGL__CAMERA_HPP__

#ifdef WIN32
#define NOMINMAX
#include <windows.h>
#endif

#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>

/**
 * \brief Class for a camera than definve the viewpoint in a virtual scene
 * \author Francois de Sorbier
 */

class Camera
{

public:

    /**
     * \brief The constructor
     */
    Camera(void);

    /**
     * \brief The destructor
     */
    virtual ~Camera(void);

//////////////////
// Setters
//////////////////

    /**
     * \brief Set the position of the camera
     *
     * \param position the new position
     */
    virtual inline void setPosition(const glm::vec3& position)
    {
        this->position = position;
    }

    /**
     * \brief Set the distance of the near clipping plan
     *
     * It should be positive
     *
     * \param near the distance of the near plane
     */
    virtual void setNear(const float& near_)
    {
        this->znear = near_;
    }

    /**
     * \brief Set the distance of the far clipping plan
     *
     * It should be positive
     *
     * \param far the distance of the far plane
     */
    virtual inline void setFar(const float& far_)
    {
        this->zfar = far_;
    }

    /**
     * \brief Set the aperture angle of the camera
     *
     * In degrees. It should be positive
     *
     * \param fovy the new aperture
     */
    virtual inline void setFOV(const float& fovy)
    {
        this->fovy = fovy;
    }

//////////////////
// Getters
//////////////////

    /**
     * \brief Return the position of the camera
     *
     * \return the position of the camera
     */
    virtual inline glm::vec3 getPosition(void) const
    {
        return this->position;
    }

    /**
     * \brief Return the front vector of the camera (Z)
     *
     * \return the front vector
     */
    virtual inline glm::vec3 getFront(void) const
    {
        return this->front;
    }

    /**
     * \brief Return the up vector of the camera (Y)
     *
     * \return the up vector
     */
    virtual inline glm::vec3 getUp(void) const
    {
        return this->up;
    }

    /**
     * \brief Return the right vector of the camera (X)
     *
     * \return the right vector
     */
    virtual inline glm::vec3 getRight(void) const
    {
        return this->right;
    }

    /**
     * \brief Return the distance of the near clipping plan
     *
     * \return the distance of the near plane
     */
    virtual inline float getNear(void) const
    {
        return this->znear;
    }

    /**
     * \brief Return the distance of the far clipping plan
     *
     * \return the distance of the far plane
     */
    virtual inline float getFar(void) const
    {
        return this->zfar;
    }

    /**
     * \brief Return the vertical Field Of View angle of the camera
     *
     * \return the vertical FOV in degrees
     */
    virtual inline float getFOV(void) const
    {
        return this->fovy;
    }

//////////////////
// Miscaleneaous
//////////////////

    /**
     * \brief Translate the position of the camera along a given direction
     *
     * \param direction the translation vector
     */
    virtual void translate(const glm::vec3& direction);

    /**
     * \brief Rotate the local coordinate system related to the camera
     *
     * \param roll angle in degrees around the Z axis
     * \param pitch angle in degrees around the X axis
     * \param yaw angle in degrees around the Y axis
     */
    virtual void rotate(const float& roll, const float& pitch, const float& yaw);

    /**
     * \brief Compute the OpenGL projection matrix for perspective projection
     *
     * \param aspect the ratio of the view width/height
     * \return the 4x4 opengl projection matrix
     */
    virtual glm::mat4 getPerspective(const float& aspect) const;

    /**
     * \brief Compute the OpenGL modelview matrix
     * \return the 4x4 opengl modelview matrix
     */
    virtual glm::mat4 getLookAt(void) const;

//////////////////
// Members
//////////////////

private:

    glm::vec3 position;

    glm::vec3 front;
    glm::vec3 up;
    glm::vec3 right;

    float znear;
    float zfar;
    float fovy;
};

#endif
